package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

	@Autowired
	private TrackInfoMapper trackInfoMapper;

	@Autowired
	private TrackStatMapper trackStatMapper;

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private VodService vodService;

	@Autowired
	private AlbumInfoService albumInfoService;

	@Autowired
	private UserInfoFeignClient userInfoFeignClient;

	@Autowired
	private RedisTemplate redisTemplate;



	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
		try {
			//跟这三张表有关系	track_info track_stat album_info;
			TrackInfo trackInfo = new TrackInfo();
			//	有相同的属性值，进行属性拷贝;
			BeanUtils.copyProperties(trackInfoVo, trackInfo);
			//	赋值用户Id
			trackInfo.setUserId(userId);
			//	order_num;声音的集数要加1有两种办法： 第一种查询当前专辑对应的声音集数+1; 第二种：查询专辑包含声音总数+1;
			AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
			trackInfo.setOrderNum(albumInfo.getIncludeTrackCount()+1);
			//	处理流媒体的数据; trackinfo表中时长，类型这些字段前端页面没有传递；要根据fileId 查询流媒体数据，然后赋值：调用云点播的api;
			TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
			//	赋值：
			trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());
			trackInfo.setMediaDuration(trackMediaInfoVo.getDuration());
			trackInfo.setMediaType(trackMediaInfoVo.getType());
			trackInfo.setMediaSize(trackMediaInfoVo.getSize());

			//	保存声音结束;
			trackInfoMapper.insert(trackInfo);

			//	track_stat
			saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
			saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
			saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
			saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);

			//	album_info; 更新数据;
			albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
			//	更新操作;
			albumInfoMapper.updateById(albumInfo);
		}catch (BeansException e) {
			log.error("保存声音信息失败：{}", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 保存声音统计数据
	 * @param trackId
	 * @param statPlay
	 */
	private void saveTrackStat(Long trackId, String statPlay) {
		TrackStat trackStat = new TrackStat();
		trackStat.setTrackId(trackId);
		trackStat.setStatType(statPlay);
		trackStat.setStatNum(new Random().nextInt(1000));
		trackStatMapper.insert(trackStat);
	}

	/**
	 * 分页查询声音列表
	 * @param trackListVoPage
	 * @param trackInfoQuery
	 * @return
	 */
	@Override
	public IPage<TrackListVo> findUserTrackPage(Page<TrackListVo> trackListVoPage, TrackInfoQuery trackInfoQuery) {
		//	调用mapper 层方法
		return trackInfoMapper.selectUserTrackPage(trackListVoPage, trackInfoQuery);

	}

	/**
	 * 删除声音，删除三个表的数据，track_info，track_stat，album_info。删除云点播的声音
	 * @param trackId
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void removeTrackInfo(Long trackId) {
		//	查询当前声音对象;
		TrackInfo trackInfo = this.getById(trackId);

		//删除track_info
		trackInfoMapper.deleteById(trackId);

		//track_stat
		trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId,trackId));

		//album_info,专辑声音总数-1
		//查询专辑信息
		AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
		//专辑声音总数-1
		albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount()-1);
		//将修改信息更新到专辑表中
		albumInfoMapper.updateById(albumInfo);
		//在重新管理一下 order_num;万一删除的是中间声音的数据，需要修改声音序号
		//update track_info set order_num = order_num - 1 where album_id = 1 and order_num > 52 and is_deleted = 0
		trackInfoMapper.updateOrderNum(trackInfo.getAlbumId(),trackInfo.getOrderNum());
		//删除云点播中的音频
		vodService.deleteMediaFile(trackInfo.getMediaFileId());
	}

	/**
	 * 修改声音
	 * @param trackId
	 * @param trackInfoVo
	 */
	@Override
	public void updateTrackInfo(Long trackId, TrackInfoVo trackInfoVo) {
		//拿到当前声音信息
		TrackInfo trackInfo = this.getById(trackId);
		//根据当前声音信息拿到当前声音的原始MediaFileId，用来判断声音是否被修改
		String originMediaFileId = trackInfo.getMediaFileId();
		//拿到原始的MediaFileId，可以将前端传来的数据进行赋值
		BeanUtils.copyProperties(trackInfoVo,trackInfo);
		//判断声音是否被修改
		if(!originMediaFileId.equals(trackInfoVo.getMediaFileId())){
			//修改了声音;
			TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
			//赋值最新流媒体数据;
			trackInfo.setMediaType(mediaInfo.getType());
			trackInfo.setMediaDuration(mediaInfo.getDuration());
			trackInfo.setMediaSize(mediaInfo.getSize());
			trackInfo.setMediaFileId(trackInfoVo.getMediaFileId());
			//删除原来的音频
			vodService.deleteMediaFile(originMediaFileId);
		}
		trackInfoMapper.updateById(trackInfo);
	}

	/**
	 * 获取专辑声音分页列表,根据专辑Id 获取到声音列表 ，将数据封装到 AlbumTrackListVo 类中
	 * @param albumId
	 * @param page
	 * @param userId
	 * @return
	 */
	/**
	 * 思路：
	 *
	 * 1. 根据专辑Id 获取到声音列表 ，将数据封装到 AlbumTrackListVo 类中
	 *
	 * 2. 判断用户是否登录
	 *
	 *    1. 用户为空的时候，只要是付费专辑，都需要在页面显示要付款的标识 {**但是，要除去试听集数**} isShowPaidMark=true
	 *
	 *       付费类型： 0101-免费  0102-vip免费  0103-vip付费
	 *
	 *    2. 用户不为空的时候
	 *       判断专辑的支付类型
	 *       	（1）专辑类型是免费 0101
	 *					什么情况都不需要付费
	 *
	 *       	（2）专辑类型属于 vip 免费类型   0102-vip免费费
	 *					判断用户如果不是vip ，则需要付费
	 *					判断用户如果是vip 但是已经过期了, 也需要显示付费
	 *
	 *       	（3）需要付费  0103-付费（无论是不是vip用户都需要购买才能使用）
	 *					需要显示付费
	 *
	 *    3. 统一处理需要付费业务
	 *
	 *       	远程调用用户微服务，去用户微服务中查询用户购买过得专辑和声音。远程调用传"专辑id","此专辑需要付费的声音列表"（返回值是Map<Long, Integer>,泛型是声音id，声音状态）
	 *       	在用户微服务的实现类中先将声音状态都改为0（设置为全部没有购买过）
	 *          获取到声音Id列表集合 与 用户购买声音Id集合进行比较  （ 将用户购买的声音存储到map中，将购买过的声音状态改为1，key=trackId value = 1或0; 1:表示购买过，0：表示没有购买过 ）
	 *          如果声音Id集合不包含购买的声音Id，则将显示为付费，否则不需要付费
	 */
	@Override
	public IPage<AlbumTrackListVo> findAlbumTrackPage(Long albumId, Page<AlbumTrackListVo> page, Long userId) {
		//根据专辑Id ，获取到声音集合列表
		IPage<AlbumTrackListVo> pageInfoList = trackInfoMapper.selectAlbumTrackPage(page,albumId);
		//拿到专辑信息，判断专辑类型用户是否需要付费：0101-免费 0102-vip付费 0103-付费
		AlbumInfo albumInfo = albumInfoService.getById(albumId);
		Assert.notNull(albumInfo,"专辑对象不能为空");
		//判断用户是否登录
		if (null==userId){
			//用户未登录
			if(!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfo.getPayType())){
				//  免费试听集数; 5
				Integer tracksForFree = albumInfo.getTracksForFree();
				//当专辑不是免费类型时，筛选出需要付费的声音列表（非试听的声音），并标记这些声音需要显示付费标识
				List<AlbumTrackListVo> albumTrackNeedPaidListVoList = pageInfoList.getRecords().stream()
						.filter(albumTrackListVo -> albumTrackListVo.getOrderNum().intValue() > albumInfo.getTracksForFree())
						.collect(Collectors.toList());

				if(!CollectionUtils.isEmpty(albumTrackNeedPaidListVoList)){
					//如果付费列表不为空，就将列表中的声音设置为true，即需要付费
					albumTrackNeedPaidListVoList.forEach(albumTrackListVo -> {
						albumTrackListVo.setIsShowPaidMark(true);
					});
				}
			}
		}else {
			//用户已经登录
			//获取用户信息
			Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
			Assert.notNull(userInfoVoResult, "查询用户信息失败");
			UserInfoVo userInfoVo = userInfoVoResult.getData();
			Assert.notNull(userInfoVo,"查询用户信息失败");
			//记录是否需要付费; false: 免费； true: 付费。
			// 设置一个变量;记录是否需要付费; false: 免费； true: 付费。设置默认值boolean isPaid=false，意味着默认免费，然后找出所有需要付费的情况将默认值改为true
			boolean isPaid = false;
			//专辑是vip免费情况下
			if(SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(albumInfo.getPayType())){
				//用户是vip且vip过期或者用户不是vip,需要付费
				if(userInfoVo.getIsVip()==1 && userInfoVo.getVipExpireTime().before(new Date()) || userInfoVo.getIsVip()==0){
					//需要付费
					isPaid=true;
				}
			}else if(SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(albumInfo.getPayType())){
				//专辑是付费的情况下，无论是不是vip用户，任何情况都需要付费
				isPaid=true;
			}
			/*else {
				//专辑免费情况，任何情况都不需要付费，直接是false，但是isPaid默认就是false，所以最后的else条件不用写
				isPaid=false;
			}*/
			//判断付费的情况
			if(isPaid){
				//取此专辑中需要付费的声音列表
				List<AlbumTrackListVo> albumTrackNeedPaidListVoList = pageInfoList.getRecords().stream()
						.filter(albumTrackListVo -> albumTrackListVo.getOrderNum().intValue() > albumInfo.getTracksForFree())
						.collect(Collectors.toList());
				//将此专辑中需要付费的声音列表id取出
				List<Long> trackIdList = albumTrackNeedPaidListVoList.stream().map(AlbumTrackListVo::getTrackId).collect(Collectors.toList());
				//远程调用，将albumId和trackIdList作为参数
				//获取用户购买的声音列表; 如果用户购买了； 将购买的声音Id与状态存储到map集合中; map.put(trackId,1)-买过; map.put(trackId,0)-未买过;
				Result<Map<Long, Integer>> mapResult = userInfoFeignClient.userIsPaidTrack(albumId, trackIdList);
				Assert.notNull(mapResult, "查询用户购买声音失败");
				Map<Long, Integer> map = mapResult.getData();
				//  map.put(trackId,1)-买过; map.put(trackId,0)-未买过;
				//将未买过的setIsShowPaidMark设置为true
				for (AlbumTrackListVo albumTrackListVo : albumTrackNeedPaidListVoList) {
					albumTrackListVo.setIsShowPaidMark(map.get(albumTrackListVo.getTrackId()) == 0);
				}
			}
		}
		return pageInfoList;
	}

	/**
	 * 修改声音播放统计数据
	 * @param trackStatMqVo
	 */
	@Override
	public void updateTrackStat(TrackStatMqVo trackStatMqVo) {
		//  1.  更新声音的播放量： update track_stat set stat_num = stat_num+1 where track_id = ? and stat_type = '0701' and is_deleted = 0;
		//  判断是否是属于播放量：
		if(SystemConstant.TRACK_STAT_PLAY.equals(trackStatMqVo.getStatType())){
			//调用更新方法
			trackInfoMapper.updateTrackStat(trackStatMqVo.getCount(),trackStatMqVo.getTrackId(),trackStatMqVo.getStatType());
			//  2.更新专辑播放量；
			albumInfoMapper.updateAlbumStat(trackStatMqVo.getCount(), trackStatMqVo.getAlbumId(), SystemConstant.ALBUM_STAT_PLAY);
		} else if (SystemConstant.TRACK_STAT_COMMENT.equals(trackStatMqVo.getStatType())) {
			//可能还会修改其他统计数据，但是目前暂无
		}
	}

	/**
	 * 获取用户声音分集购买支付列表
	 * 1.  从当前点击的声音开始，计算有多少需要付费的声音列表;
	 * 2.  查询用户是否购买过当前专辑对应的声音，然后与1步骤进行过滤;
	 * @param trackId
	 * @return
	 */
	@Override
	public List<Map<String, Object>> findUserTrackPaidList(Long trackId, Long userId) {
		List<Map<String, Object>> list=new ArrayList<>();
		//获取当前声音对象
		TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
		//获取到当前用户购买的声音列表
		Result<List<Long>> trackIdListResult = userInfoFeignClient.findUserPaidTrackList(trackInfo.getAlbumId());
		Assert.notNull(trackIdListResult,"查询用户购买声音列表失败");
		//用户购买的此专辑下的声音列表
		List<Long> trackIdList = trackIdListResult.getData();
		Assert.notNull(trackIdList,"查询用户购买声音列表失败");
		//获取当前声音的排序值
		Integer orderNum = trackInfo.getOrderNum();
		//从当前声音开始计算有多少声音是需要付费的声音集合;
		//select * from track_info where order_num > 8 and album_id = 341;
		List<TrackInfo> trackInfoList = trackInfoMapper.selectList(new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getAlbumId, trackInfo.getAlbumId()).gt(TrackInfo::getOrderNum, orderNum));

		//两个集合取差集，算出需要付费的声音列表
		List<TrackInfo> paidTrackIdList = trackInfoList.stream().filter(trackInfoItem -> !trackIdList.contains(trackInfoItem.getId())).collect(Collectors.toList());
		//通过声音对象拿到专辑id，获取专辑对象,获取专辑的声音价格
		AlbumInfo albumInfo = albumInfoService.getById(trackInfo.getAlbumId());
		//paidTrackIdList.size() >= 0 这个条件其实永远为真（因为集合的大小不可能是负数），这意味着这段代码会无条件执行，即无论 paidTrackIdList 包含多少元素，都会往 list 中添加一个代表 “本集” 的条目
		if(paidTrackIdList.size()>=0){
			//显示本集
			Map<String, Object> map=new HashMap<>();
			map.put("name", "本集");
			//  当前每一集声音的钱数;
			map.put("price", albumInfo.getPrice());
			map.put("trackCount", 0);
			//  添加本集到集合;
			list.add(map);
		}
		//后size集，就是需要付费的声音集数剩下不到十集，剩下几集显示几集
		if(paidTrackIdList.size()>0 && paidTrackIdList.size()<10){
			//后size集
			Map<String, Object> map=new HashMap<>();
			map.put("name", "后"+paidTrackIdList.size()+"集");
			//  当前每一集声音的钱数;
			map.put("price", albumInfo.getPrice().multiply(new BigDecimal(String.valueOf(paidTrackIdList.size()))));
			map.put("trackCount", paidTrackIdList.size());
			//  添加本集到集合;
			list.add(map);
		}
		//后十集，最后剩下需要付费的超过十集就会有后十集这个选项
		if(paidTrackIdList.size()>10){
			//后10集
			Map<String, Object> map=new HashMap<>();
			map.put("name", "后10集");
			//  当前每一集声音的钱数;
			map.put("price", albumInfo.getPrice().multiply(new BigDecimal("10")));
			map.put("trackCount", 10);
			//  添加本集到集合;
			list.add(map);
		}
		//后十多集
		if (paidTrackIdList.size() > 10 && paidTrackIdList.size() <= 20) {
			//  显示后size集;
			HashMap<String, Object> map = new HashMap<>();
			map.put("name", "后" + paidTrackIdList.size() + "集");
			//  当前每一集声音的钱数;
			map.put("price", albumInfo.getPrice().multiply(new BigDecimal(String.valueOf(paidTrackIdList.size()))));
			map.put("trackCount", paidTrackIdList.size());
			//  添加本集到集合;
			list.add(map);
		}
		//  后20集;
		if (paidTrackIdList.size() > 20) {
			//  后20集合;
			HashMap<String, Object> map = new HashMap<>();
			map.put("name", "后20集");
			//  当前每一集声音的钱数;
			map.put("price", albumInfo.getPrice().multiply(new BigDecimal("20")));
			map.put("trackCount", 20);
			//  添加本集到集合;
			list.add(map);
		}
		//返回数据
		return list;
	}

	/**
	 * 批量获取下单付费声音列表
	 * @param trackId
	 * @param trackCount
	 * @return
	 */
	@Override
	public List<TrackInfo> findPaidTrackInfoList(Long trackId, Integer trackCount, Long userId) {
		List<TrackInfo> trackInfoList =new ArrayList<>();
		TrackInfo trackInfo = trackInfoMapper.selectById(trackId);
		//获取用户购买的声音集合列表
		Result<List<Long>> trackIdListResult = userInfoFeignClient.findUserPaidTrackList(trackInfo.getAlbumId());
		Assert.notNull(trackIdListResult,"查询用户购买声音列表失败");
		List<Long> userPaidTrackIdList = trackIdListResult.getData();
		Assert.notNull(userPaidTrackIdList,"查询用户购买声音列表失败");
		if(0==trackCount){
			//说明购买的是本集
			trackInfoList.add(trackInfo);
		}else {
			//select * from track_info where album_id=? and order_num>? and not in ();
			LambdaQueryWrapper<TrackInfo> wrapper = new LambdaQueryWrapper<>();
			wrapper.eq(TrackInfo::getAlbumId,trackInfo.getAlbumId())
					.gt(TrackInfo::getOrderNum,trackInfo.getOrderNum());
			if(!CollectionUtils.isEmpty(userPaidTrackIdList)){
				wrapper.notIn(TrackInfo::getId,userPaidTrackIdList);
			}
			wrapper.last(" limit "+trackCount);
			//  查询数据;
			trackInfoList = trackInfoMapper.selectList(wrapper);
		}
		//返回数据
		return trackInfoList;
	}
}
